#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <strings.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>

#include "logicbox.h"

#define BAUDRATE B9600
//#define BAUDRATE B38400
//#define BAUDRATE B4000000

// *************************************
// USB functions
// *************************************

int usbOpen(const char * device)
{
    int fd;
    struct termios newtio;
    //struct termios oldtio;

    fd = open(device, O_RDWR | O_NOCTTY); 
    if (fd <0) {
        perror(device); 
        exit(-1); 
    }

    //tcgetattr(fd, &oldtio); /* save current port settings */

    /* set input mode (non-canonical, no echo,...) */
    bzero(&newtio, sizeof(newtio));
    newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
    newtio.c_iflag = IGNPAR;
    newtio.c_iflag = 0;
    newtio.c_oflag = 0;
    newtio.c_lflag = 0;
    newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
    newtio.c_cc[VMIN]     = 1;   /* blocking read until 1 chars received */
    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, &newtio);

    return fd;
}


int usbClose(int fd)
{
    close(fd);
    return 0;
}



// *************************************
// general LogicBox/LogicPool functions
// *************************************


void logicReset(int fd)
{
    write(fd, "R", 1);
}


uint32_t logicGetID(int fd)
{
    int buf, res;

    write(fd, "#", 1);
    res = read(fd, &buf, 4);
    return ntohl(buf);
}


void logicSetAddress(int fd, uint8_t type, uint8_t number, uint8_t addr)
{
    uint8_t buf[20];

    buf[0] = 'A';
    buf[1] = 0;
    buf[2] = type;
    buf[3] = number;
    buf[4] = addr;
    write(fd, buf, 5);
}


void logicSetShortAddress(int fd, uint8_t number, uint8_t addr)
{
    uint8_t buf[20];

    buf[0] = 'M';
    buf[1] = number;
    buf[2] = addr;
    write(fd, buf, 3);
}


uint8_t logicReadByte(int fd)
{
    uint8_t buf;

    write(fd, "b", 1);
    read(fd, &buf, 1);
    return buf;
}


int logicWriteByte(int fd, uint8_t data)
{
    uint8_t buf[2];

    buf[0] = 'B';
    buf[1] = data;
    write(fd, buf, 2);
    return 0;
}


uint16_t logicReadWord(int fd)
{
    uint16_t buf;

    write(fd, "w", 1);
    read(fd, &buf, 2);
    //fprintf(stdout,"\nbu f=%i",ntohs(buf) & 0x03ff);
    return ntohs(buf);
}


void logicReadWordFIFO(int fd, uint16_t * data, uint16_t n)
{
    uint8_t buf[20];
    int i, m;

    // set up FIFO read command
    buf[0] = 'F';
    buf[1] = (n >> 8) & 0xff;
    buf[2] = (n >> 0) & 0xff;
    buf[3] = 'w';
    write(fd, buf, 4);

    // read data from device, read until we have enough data
    for (m = 0; m < 2*n; ) 
      m += read(fd, &(data[m/2]), 2 * n - m);

    // adjust byte order of the data 
    for (i = 0; i < n; i++)
        data[i] = ntohs(data[i]);
}


int logicWriteWord(int fd, uint16_t data)
{
    uint8_t buf[3];

    buf[0] = 'W';
    buf[1] = (data >> 8) & 0xff;
    buf[2] = (data >> 0) & 0xff;
    write(fd, buf, 3);
    return 0;
}


uint32_t logicReadLong(int fd)
{
    uint32_t buf;

    write(fd, "l", 1);
    read(fd, &buf, 4);
    return ntohl(buf);
}


int logicWriteLong(int fd, uint32_t data)
{
    uint8_t buf[5];

    buf[0] = 'L';
    buf[1] = (data >> 24) & 0xff;
    buf[2] = (data >> 16) & 0xff;
    buf[3] = (data >>  8) & 0xff;
    buf[4] = (data >>  0) & 0xff;
    write(fd, buf, 5);
    return 0;
}


void logicListDevices(int fd)
{
    int i, k;
    const int chars = 'Z'-'A'+1;
    uint8_t * deviceTypes;

    // create a list of device types: capital chars from 'A' to 'Z'
    deviceTypes = (uint8_t *) malloc(chars);
    bzero(deviceTypes, chars);
    for (i = 0; i < chars; i++)
        deviceTypes[i] = i + 'A';

    // probe all device types in the deviceTypes list
    for (i = 0; i < strlen(deviceTypes); i++) {
        uint8_t device = deviceTypes[i];

        // device numbers start at one
        for (k = 1; k < 255; k++) {
            uint8_t mux;

            // read mux at address zero
            logicSetAddress(fd, device, k, 0);
            mux = logicReadByte(fd);

            // mux is 255 if there's no such device, 
            // assume proper ordering of device numbers
            if (mux == (uint8_t) 0xff)
                break;

            printf("Device type %c, #%i: mux = %x, type = %s\n", 
                device, k, mux, logicDeviceName(device));
        }
    }
}


char * logicDeviceName(uint8_t device)
{
    switch(device) {
        case 'L': return "logic module";
        case 'Q': return "QDC";
        case 'T': return "TTL connector";
        case 'I': return "indicator";
        default: return "unknown";
    }
}

